#pragma once

#include <iostream>
#include <pthread.h>
#include <queue>
#include <mutex>
#include <unistd.h>
#include "lockGuard.hpp"

using namespace std;

const int gDefaultCap = 5;

template <class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return _bq.size() == 0;
    }
    bool isQueueFull()
    {
        return _bq.size() == _capacity;
    }
public:
    BlockQueue(size_t capacity = gDefaultCap)
        :_capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_Empty, nullptr);
        pthread_cond_init(&_Full, nullptr);
    }

    void push(const T& in)
    {
        // pthread_mutex_lock(&_mtx);
        // // 1. 先检测当前临界资源是否满足条件

        // // 【注意】：如果pthread_cond_wait失败了怎么办呢，又没有接受返回值判断
        // //          函数调用失败就不会阻塞，这里使用的queue容器，如果使用固定长度的数组就会越界
        // //          或者这个线程因为一些原因，但不应该被唤醒，这叫做伪唤醒，也会出问题
        // //          所以一定要使用while判断条件，只要队列中有数据就阻塞
        // while (isQueueFull())
        // {
        //     pthread_cond_wait(&_Full, &_mtx); // 满了的时候就不要放了
        // }
        // // 2. 访问临界资源
        // _bq.push(in);
        // // 放好了数据就要让消费者再拿空
        // pthread_cond_signal(&_Empty);

        // pthread_mutex_unlock(&_mtx);

        lockGuard lockguard(&_mtx); //可以自动调用构造和析构函数来加锁和解锁

        while (isQueueFull()) pthread_cond_wait(&_Full, &_mtx); // 满了的时候就不要放了
        _bq.push(in);
        pthread_cond_signal(&_Empty);
    }

    void pop(T* out)
    {
        // pthread_mutex_lock(&_mtx);

        // while (isQueueEmpty()) pthread_cond_wait(&_Empty, &_mtx); // 空了的时候就不要拿了
        // *out = _bq.front();
        // _bq.pop();
        // // 拿走了数据就要让生产者再放满
        // pthread_cond_signal(&_Full);

        // pthread_mutex_unlock(&_mtx);

        lockGuard lockguard(&_mtx);

        while (isQueueEmpty()) pthread_cond_wait(&_Empty, &_mtx); // 空了的时候就不要拿了
        *out = _bq.front();
        _bq.pop();
        pthread_cond_signal(&_Full);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Empty);
        pthread_cond_destroy(&_Full);
    }

private:
    queue<T> _bq;            // 阻塞队列
    size_t _capacity;        // 容量上限
    pthread_mutex_t _mtx;    // 互斥锁，保证队列安全
    pthread_cond_t _Empty; // 条件变量，用来表示bq是否为空
    pthread_cond_t _Full;  // 表示bq是否满了
};